package org.teamh.gui.mapeditor.filechooser;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;


import de.lessvoid.nifty.Nifty;
import de.lessvoid.nifty.builder.ImageBuilder;
import de.lessvoid.nifty.builder.PanelBuilder;
import de.lessvoid.nifty.builder.PopupBuilder;
import de.lessvoid.nifty.controls.Label;
import de.lessvoid.nifty.controls.TextField;
import de.lessvoid.nifty.controls.button.builder.ButtonBuilder;
import de.lessvoid.nifty.controls.label.builder.LabelBuilder;
import de.lessvoid.nifty.controls.scrollpanel.builder.ScrollPanelBuilder;
import de.lessvoid.nifty.controls.textfield.builder.TextFieldBuilder;
import de.lessvoid.nifty.elements.Element;
import de.lessvoid.nifty.elements.render.TextRenderer;
import de.lessvoid.nifty.layout.align.HorizontalAlign;
import de.lessvoid.nifty.screen.Screen;
import de.lessvoid.nifty.tools.Color;

public class NiftyFileChooser {
	private static final int BUTTON_HOME = 1;
	private static final int BUTTON_PREVIOUS = 2;
	
	private String id;
	
	private File currentFile;
	
	private Nifty nifty;
	private Screen screen;
	
	private Element popup;
	
	private File oldWorkingFile;
	private File workingFile;
	
	private HashMap<String, NiftyFile> fileCache;
	
	private NiftyFileChooseInvoker niftyFileChooseInvoker;
	
	private boolean setOpen = false;
	private boolean open = false;
	
	private String[] extensions;
	
	public NiftyFileChooser(Nifty nifty, Screen screen) {
		this.nifty = nifty;
		this.screen = screen;
		id = "nifty_file_chooser_" + Math.random();
		fileCache = new HashMap<String, NiftyFile>();
		
		init();
	}
	
	private void init() {
		new PopupBuilder(id) {{
			childLayoutCenter();
			backgroundColor("#000a");
			
			//begin outer panel
			panel(new PanelBuilder() {{
				childLayoutVertical();
				width("653px");
				height("550px");
				style("nifty-panel");
				
				//begin upper panel for name and button
				panel(new PanelBuilder(){{
					width("620px");
					height("50px");
					
					childLayoutHorizontal();
					
					control(new LabelBuilder(id + "_currentFile") {{
						alignLeft();
//						textHAlignRight();
						valignCenter();
						color("#fff");
						text("");
						width("400px");
					}});
					
					panel(new PanelBuilder() {{
//						backgroundColor("#010");
						width("20px");
					}});
					
					image(new ImageBuilder() {{
						valignCenter();
						width("32px");
						height("32px");
						filename("res/img/mapeditor/home.png");
						visibleToMouse();
						interactOnClick("niftyFileChooserClick(" + BUTTON_HOME + ")");
					}});
					
					panel(new PanelBuilder() {{
//						backgroundColor("#010");
						width("20px");
					}});
					
					image(new ImageBuilder() {{
						valignCenter();
						width("32px");
						height("32px");
						filename("res/img/mapeditor/folder_up.png");
						visibleToMouse();
						interactOnClick("niftyFileChooserClick(" + BUTTON_PREVIOUS + ")");
					}});
				}});
				//end upper panel
				
				//begin center panel for displaying files
				control(new ScrollPanelBuilder(id + "central_scroll") {{
					style("nifty-listbox");
					width("*");
					height("*");
					backgroundColor("#444");
					set("vertical", "false");
					panel(new PanelBuilder(id + "_central") {{
						childLayoutAbsolute();
					}});
					visibleToMouse();
					interactOnClick("niftyFileChooserClick(deselect)");
				}});
				//end center panel
				
				//begin panel for displaying the selected file
				panel(new PanelBuilder() {{
					width("*");
					height("50px");
					childLayoutCenter();
					
					control(new TextFieldBuilder("nifty_filechooser_name") {{
						width("200px");
						visibleToMouse();
						interactOnClick("niftyFileChooserClick(deselect)");
					}});
				}});
				//end panel for displaying selected file
				
				//begin lower panel for ok and cancel
				panel(new PanelBuilder(){{
					width("620px");
					height("50px");
					
					childLayoutHorizontal();
					
					panel(new PanelBuilder() {{
						childLayoutCenter();
						width("*");
						height("*");
						control(new ButtonBuilder(id + "_ok", "OK") {{
							visibleToMouse();
							interactOnClick("niftyFileChooserOK()");
						}});
					}});
					
					panel(new PanelBuilder() {{
						childLayoutCenter();
						width("*");
						height("*");
						control(new ButtonBuilder(id + "_cancel", "Cancel") {{
							visibleToMouse();
							interactOnClick("niftyFileChooserCancel()");
						}});
					}});
				}});
				//end lower panel
			}});
			//end outer panel
		}}.registerPopup(nifty);
		
		popup = nifty.createPopup(id);
	}
	
	private void show() {
		nifty.showPopup(screen, popup.getId(), null);
	}
	
	private void hide() {
		nifty.closePopup(popup.getId());
	}
	
	public void open(NiftyFileChooseInvoker niftyFileChooseInvoker, String[] extensions, boolean open) {
		open(niftyFileChooseInvoker, null, extensions, open);
	}
	
	public void open(NiftyFileChooseInvoker niftyFileChooseInvoker, File file, String[] extensions, boolean open) {
		this.niftyFileChooseInvoker = niftyFileChooseInvoker;
		this.extensions = extensions;
		this.open = open;
		this.setOpen = true;
		
		//Ensure, that the file is not null or does not exist. 
		if(file == null || !file.exists()) {
			if(currentFile != null && currentFile.exists()) {
				file = currentFile;
			} else {
				file = new File(System.getProperty("user.home"));
			}
		}
		
		if(file.exists() && !file.isDirectory()) {
			file = file.getParentFile();
		}
		
		show();
		
		workingFile = file;
	}
	
	public void ok() {
		workingFile = currentFile;
		
		if(open && currentFile == null) {
			String text = screen.findNiftyControl("nifty_filechooser_name", TextField.class).getText();
			File f = new File(oldWorkingFile.getPath(), text);
			
			if(!isValid(f)) {
				f = new File(oldWorkingFile.getPath(), text + "." + extensions[0]);
			}
			
			if(!f.exists()) {
				try {
					f.createNewFile();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			workingFile = f;
		}
	}
	
	public void cancel() {
		hide();	
	}
	
	public void clicked(String click) {
		if(click.startsWith("double")) {
			String[] list = click.split(" ");
			
			if(fileCache.get(list[1]).isDoubleClick()) {
				workingFile = fileCache.get(list[1]).getFile();
			} else {
				for(String s: fileCache.keySet()) {
					screen.findNiftyControl(s, Label.class).setColor(new Color("#fff"));
				}
				
				screen.findNiftyControl(list[1], Label.class).setColor(new Color("#f00"));
				currentFile = fileCache.get(list[1]).getFile();
				
				if(!currentFile.isDirectory()) {
					screen.findNiftyControl("nifty_filechooser_name", TextField.class).setText(currentFile.getName());
				}
			}
		} else if(click.startsWith("deselect")) {
			for(String s: fileCache.keySet()) {
				screen.findNiftyControl(s, Label.class).setColor(new Color("#fff"));
			}
			
			currentFile = null;
		} else {
			switch(Integer.parseInt(click)) {
			case BUTTON_HOME: workingFile = new File(System.getProperty("user.home")); break;
			case BUTTON_PREVIOUS: workingFile = oldWorkingFile.getParentFile(); break;
			}
		}
	}
	
	public void render() {
		if(workingFile != null) {
			updateChooser(workingFile);
			
			oldWorkingFile = workingFile;
			workingFile = null;
		}
		
		if(setOpen) {
			TextField t = screen.findNiftyControl("nifty_filechooser_name", TextField.class);
			t.setText("");
			Element e = t.getElement();
			
			if(open) {
				e.enable();
			} else {
				e.disable();
			}
			
			setOpen = false;
		}
	}
	
	private void updateChooser(File file) {
		if(file.isDirectory()) {
			//update the text to display
			Label name = screen.findNiftyControl(id + "_currentFile", Label.class);
			name.setText(file.getAbsolutePath());
			int width = name.getWidth();
			TextRenderer tr = new TextRenderer(nifty, nifty.getRenderEngine().getFont(), file.getAbsolutePath());
			int textWidth = tr.getTextWidth();
			if(textWidth > width) {
				name.getElement().getRenderer(TextRenderer.class).setTextHAlign(HorizontalAlign.right);
			} else {
				name.getElement().getRenderer(TextRenderer.class).setTextHAlign(HorizontalAlign.center);
			}
			
			screen.findElementByName(id + "_ok").setFocus();
			
			Element element = screen.findElementByName(id + "_central");
			
			clear(element);
			fileCache.clear();
			
			int totalWidth = 2;
			int maxWidth = 0;
			File[] files = listFiles(file);
			for(int i = 0; i < files.length; i++) {
				if(i % 17 == 0) {
					totalWidth += (maxWidth + 10);
					maxWidth = 0;
				}
				Element e = null;
				
				if(files[i].isDirectory()) {
					e = createDirectory(totalWidth, i, files[i], element);
				} else {
					e = createFile(totalWidth, i, files[i], element);
				}
				maxWidth = Math.max(e.getWidth(), maxWidth);
				element.add(e);
			}
			
			element.setWidth(totalWidth + maxWidth); 
		} else {
			hide();
			
			niftyFileChooseInvoker.invokeWhenFinished(file);
		}
	}
	
	private void clear(Element e) {
		for(Element el: e.getElements()) {
			el.setVisible(false);
			el.markForRemoval();
		}
	}
	
	private Element createDirectory(int width, int counter, final File file, Element parent) {
		final int x = width;
		final int y = (counter % 17) * 20;
		final String id = Math.random() + "";
		
		final TextRenderer tr = new TextRenderer(nifty, nifty.getRenderEngine().getFont(), file.getName());
		
		Element e = new PanelBuilder() {{
			childLayoutHorizontal();
			visibleToMouse();
			x(x + "px");
			y(y + "px");
			width((22 + tr.getTextWidth()) + "px");
			height("20px");
			String param = "double " + id;
			interactOnClick("niftyFileChooserClick(" + param + ")");
			
			image(new ImageBuilder() {{
				valignCenter();
				width("20px");
				height("20px");
				filename("res/img/mapeditor/folder.png");
				
			}});
			
			panel(new PanelBuilder() {{
				width("2px");
			}});
			
			control(new LabelBuilder(id) {{
				textHAlignLeft();
				text(file.getName());
				color("#fff");
				width(tr.getTextWidth() + "px");
			}});
		}}.build(nifty, screen, parent);
		
		fileCache.put(id, new NiftyFile(file));
		
		return e;
	}
	
	private Element createFile(int width, int counter, final File file, Element parent) {
		final int x = width;
		final int y = (counter % 17) * 20;
		final String id = Math.random() + "";
		
		final TextRenderer tr = new TextRenderer(nifty, nifty.getRenderEngine().getFont(), file.getName());
		
		Element e = new LabelBuilder(id) {{
			textHAlignLeft();
			text(file.getName());
			color("#fff");
			x(x + "px");
			y(y + "px");
			width(tr.getTextWidth() + "px");
			String param = "double " + id;
			interactOnClick("niftyFileChooserClick(" + param + ")");
		}}.build(nifty, screen, parent);
		
		fileCache.put(id, new NiftyFile(file));
		
		return e;
	}
	
	private File[] listFiles(File f) {
		File[] all = f.listFiles();
		
		if(all == null) {
			return new File[0];
		}
		
		ArrayList<File> folders = new ArrayList<File>();
		ArrayList<File> files = new ArrayList<File>();
		
		for(int i = 0; i < all.length; i++) {
			if(!all[i].isHidden()) {
				if(all[i].isDirectory()) {
					folders.add(all[i]);
				} else {
					if(isValid(all[i])) {
						files.add(all[i]);
					}
				}
			}
		}
		
		File[] foldersArray = new File[folders.size()];
		File[] filesArray = new File[files.size()];
		
		folders.toArray(foldersArray);
		files.toArray(filesArray);
		
		Arrays.sort(foldersArray, new FileComparator());
		Arrays.sort(filesArray, new FileComparator());
		
		File[] res = new File[foldersArray.length + filesArray.length];
		int i = 0;
		for(; i < foldersArray.length; i++) {
			res[i] = foldersArray[i];
		}
		for(int j = 0; j < filesArray.length; i++, j++) {
			res[i] = filesArray[j];
		}
		
		return res;
	}
	
	private boolean isValid(File f) {
		if(extensions == null) {
			return true;
		}
		
		for(int i = 0; i < extensions.length; i++) {
			String name = f.getName();
			
			int lastIndex = name.lastIndexOf(".");
			
			if(lastIndex >= 0) {
				lastIndex++;
				
				if(name.substring(lastIndex).equals(extensions[i])) {
					return true;
				}
			}
		}
		
		return false;
	}
	
	public static interface NiftyFileChooseInvoker {
		public void invokeWhenFinished(File file);
	}
}
